package sk.stuba.fiit.repka.dp.owl;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.util.SimpleIRIMapper;

public class OWLCreationManager {
//	private List<OWLComponent> owlComponents;
//	private static final IRI example_iri = IRI.create("http://127.0.0.1/test.owl");
	private IRI example_iri;
	
//	public List<OWLComponent> getOWLComponents() {
//		if(this.owlComponents == null) {
//			this.owlComponents = new ArrayList<OWLComponent>();
//		}
//		return this.owlComponents;
//	}
	
	private OWLDataFactory df;
	private OWLOntologyManager manager;
	private File output;
	private IRI documentIRI;
	private SimpleIRIMapper mapper;
	private OWLOntology ontology;

	public OWLCreationManager(String ontologyName) throws OWLOntologyCreationException {
		this.example_iri = IRI.create("http://127.0.0.1/"+ontologyName+".owl");
		this.output = new File("." + File.separator + "ontology" + File.separator + ontologyName +".owl");
		this.documentIRI = IRI.create(output);
		
		this.df = OWLManager.getOWLDataFactory();
		this.manager = OWLManager.createOWLOntologyManager();
		this.mapper = new SimpleIRIMapper(example_iri, documentIRI);
		this.manager.addIRIMapper(mapper);
		this.ontology = this.manager.createOntology(example_iri);
	}
	
	public String createOntology(List<MyOWLClass> classes, List<MyOWLObjectProperty> objProperties, List<MyOWLDataProperty> dataProperties, List<MyOWLIndividual> individuals) throws OWLOntologyCreationException, OWLOntologyStorageException, IOException {
		this.createClasses(null, classes);
		this.createObjectProperties(null, objProperties);
		this.createDataProperties(null, dataProperties);
		this.addDisjoinToClasses(classes);
		this.addRestricitonsToClasses(classes);
		this.createIndividuals(individuals);
		return this.example_iri.toString();
	}
	
	private void  createClasses(MyOWLClass parentClass, List<? extends OWLComponent> classes) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = classes.iterator();
		while(iter.hasNext()) {
			 MyOWLClass cls = (MyOWLClass)iter.next();
			 if(parentClass == null && !cls.hasSubComponent()) {
				 OWLClass ontoCls = df.getOWLClass(IRI.create(cls.getName()));
				 OWLDeclarationAxiom declarationAxiom = df.getOWLDeclarationAxiom(ontoCls);
				 manager.addAxiom(ontology, declarationAxiom);
				 manager.saveOntology(ontology);
			 } else if(parentClass != null){
				 OWLClass superOntoCls = df.getOWLClass(IRI.create(parentClass.getName()));
				 OWLClass ontoCls = df.getOWLClass(IRI.create(cls.getName()));
				 OWLAxiom axiom = df.getOWLSubClassOfAxiom(ontoCls, superOntoCls);
				 manager.addAxiom(ontology, axiom);
				 manager.saveOntology(ontology);
			 }
			 if(cls.hasSubComponent()) {
				 this.createClasses(cls, cls.getSubComponents());
			 }
		}
	}
	
	private void addRestricitonsToClasses(List<? extends OWLComponent> classes) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = classes.iterator();
		while(iter.hasNext()) {
			MyOWLClass cls = (MyOWLClass)iter.next();
			if(cls.hasRestriction()) {
				for(OWLRestriction restriction : cls.getRestrictions()) {
					OWLObjectProperty restProp = df.getOWLObjectProperty(IRI.create(restriction.getObject().getName()));
					OWLClass restClass = df.getOWLClass(IRI.create(restriction.getClazz().getName()));

					OWLClassExpression expression = null;
					if (restriction.isUseSomeValues()) {
						expression = df.getOWLObjectSomeValuesFrom(restProp, restClass);
					} else if (restriction.isUseAllValues()) {
						expression = df.getOWLObjectAllValuesFrom(restProp, restClass);
					}

					OWLClass head = df.getOWLClass(IRI.create(cls.getName()));
					// Head subclass of our restriction
					OWLSubClassOfAxiom axiom = df.getOWLSubClassOfAxiom(head, expression);
					manager.addAxiom(ontology, axiom);
					manager.saveOntology(ontology);
				}
			}
			if(cls.hasSubComponent()) {
				this.addRestricitonsToClasses(cls.getSubComponents());
			}
		}
	}
	
	private void addDisjoinToClasses(List<? extends OWLComponent> classes) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = classes.iterator();
		while(iter.hasNext()) {
			MyOWLClass cls = (MyOWLClass)iter.next();
			if(cls.hasDisjoinClass()) {
				OWLClass ontoCls = df.getOWLClass(IRI.create(cls.getName()));
				for(MyOWLClass disjoin : cls.getDisjoinClasses()) {
					OWLClass ontoDisjoinCls = df.getOWLClass(IRI.create(disjoin.getName()));
					OWLDisjointClassesAxiom axiom = df.getOWLDisjointClassesAxiom(ontoCls, ontoDisjoinCls);
					manager.addAxiom(ontology, axiom);
					manager.saveOntology(ontology);
				}
			}
			if(cls.hasSubComponent()) {
				this.addDisjoinToClasses(cls.getSubComponents());
			}
		}
	}
	
	private void createDataProperties(MyOWLDataProperty parentProp, List<? extends OWLComponent> dataProperties) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = dataProperties.iterator();
		while(iter.hasNext()) {
			 MyOWLDataProperty prop = (MyOWLDataProperty)iter.next();
			 if(parentProp == null && !prop.hasSubComponent()) {
				 OWLDataProperty ontoProp = df.getOWLDataProperty(IRI.create(prop.getName()));
				 OWLDeclarationAxiom declarationAxiom = df.getOWLDeclarationAxiom(ontoProp);
				 manager.addAxiom(ontology, declarationAxiom);
				 manager.saveOntology(ontology);
			 } else if(parentProp != null){
				 OWLDataProperty superOntoProp = df.getOWLDataProperty(IRI.create(parentProp.getName()));
				 OWLDataProperty ontoProp = df.getOWLDataProperty(IRI.create(prop.getName()));
				 OWLAxiom axiom = df.getOWLSubDataPropertyOfAxiom(ontoProp, superOntoProp);
				 manager.addAxiom(ontology, axiom);
				 manager.saveOntology(ontology);
			 }
			 if(prop.hasSubComponent()) {
				 this.createDataProperties(prop, prop.getSubComponents());
			 }
		}
	}
	
	private void createObjectProperties(MyOWLObjectProperty parentProp, List<? extends OWLComponent> objProperties) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = objProperties.iterator();
		while(iter.hasNext()) {
			MyOWLObjectProperty prop = (MyOWLObjectProperty)iter.next();
			 if(parentProp == null && !prop.hasSubComponent()) {
				 OWLObjectProperty ontoProp = df.getOWLObjectProperty(IRI.create(prop.getName()));
				 OWLDeclarationAxiom declarationAxiom = df.getOWLDeclarationAxiom(ontoProp);
				 manager.addAxiom(ontology, declarationAxiom);
				 manager.saveOntology(ontology);
			 } else if(parentProp != null){
				 OWLObjectProperty superOntoProp = df.getOWLObjectProperty(IRI.create(parentProp.getName()));
				 OWLObjectProperty ontoProp = df.getOWLObjectProperty(IRI.create(prop.getName()));
				 OWLAxiom axiom = df.getOWLSubObjectPropertyOfAxiom(ontoProp, superOntoProp);
				 manager.addAxiom(ontology, axiom);
				 manager.saveOntology(ontology);
			 }
			 if(prop.hasSubComponent()) {
				 this.createObjectProperties(prop, prop.getSubComponents());
			 }
		}
	}
	
	private void addRangesToObjProp(List<? extends OWLComponent> properties) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = properties.iterator();
		while(iter.hasNext()) {
			MyOWLObjectProperty objProp = (MyOWLObjectProperty)iter.next();
			if(objProp.getRanges().size() > 0) {
				for(String range : objProp.getRanges()) {
					OWLClass ontoCls = df.getOWLClass(IRI.create(range));
					OWLObjectProperty ontoProp = df.getOWLObjectProperty(IRI.create(objProp.getName()));
					OWLObjectPropertyRangeAxiom axiomrange = df.getOWLObjectPropertyRangeAxiom(ontoProp, ontoCls);
					manager.addAxiom(ontology, axiomrange);
					manager.saveOntology(ontology);
				}
			}
			if(objProp.hasSubComponent()) {
				this.addRangesToObjProp(objProp.getSubComponents());
			}
		}
	}
	
	private void addDomainsToObjProp(List<? extends OWLComponent> properties) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = properties.iterator();
		while(iter.hasNext()) {
			MyOWLObjectProperty objProp = (MyOWLObjectProperty)iter.next();
			if(objProp.getDomains().size() > 0) {
				for(MyOWLClass c : objProp.getDomains()) {
					OWLClass ontoCls = df.getOWLClass(IRI.create(c.getName()));
					OWLObjectProperty ontoProp = df.getOWLObjectProperty(IRI.create(objProp.getName()));
					OWLObjectPropertyDomainAxiom axiomdomain = df.getOWLObjectPropertyDomainAxiom(ontoProp, ontoCls);
					manager.addAxiom(ontology, axiomdomain);
					manager.saveOntology(ontology);
				}
			}
			if(objProp.hasSubComponent()) {
				this.addDomainsToObjProp(objProp.getSubComponents());
			}
		}
	}
	
	private void addRangesToDataProp(List<? extends OWLComponent> properties) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = properties.iterator();
		while(iter.hasNext()) {
			MyOWLDataProperty dataProp = (MyOWLDataProperty)iter.next();
			if(dataProp.getRanges().size() > 0) {
				for(String range : dataProp.getRanges()) {
					OWLDataProperty ontoProp = df.getOWLDataProperty(IRI.create(dataProp.getName()));
					OWLDataRange owldatarange = df.getOWLDatatype(IRI.create("http://www.w3.org/2001/XMLSchema#" + range));
					OWLDataPropertyRangeAxiom axiomrange = df.getOWLDataPropertyRangeAxiom(ontoProp, owldatarange);
					manager.addAxiom(ontology, axiomrange);
					manager.saveOntology(ontology);
				}
			}
			if(dataProp.hasSubComponent()) {
				this.addRangesToDataProp(dataProp.getSubComponents());
			}
		}
	}
	
	private void addDomainsToDataProp(List<? extends OWLComponent> properties) throws OWLOntologyStorageException {
		Iterator<? extends OWLComponent> iter = properties.iterator();
		while(iter.hasNext()) {
			MyOWLDataProperty dataProp = (MyOWLDataProperty)iter.next();
			if(dataProp.getDomains().size() > 0) {
				for(MyOWLClass c : dataProp.getDomains()) {
					OWLClass ontoCls = df.getOWLClass(IRI.create(c.getName()));
					OWLDataProperty ontoProp = df.getOWLDataProperty(IRI.create(dataProp.getName()));
					OWLDataPropertyDomainAxiom axiomdomain = df.getOWLDataPropertyDomainAxiom(ontoProp, ontoCls);
					manager.addAxiom(ontology, axiomdomain);
					manager.saveOntology(ontology);
				}
			}
			if(dataProp.hasSubComponent()) {
				this.addDomainsToDataProp(dataProp.getSubComponents());
			}
		}
	}
	
	private void createIndividuals(List<MyOWLIndividual> individuals) throws OWLOntologyStorageException {
		Iterator<MyOWLIndividual> iter = individuals.iterator();
		while(iter.hasNext()) {
			MyOWLIndividual indi = iter.next();
			OWLIndividual ontoIndi = df.getOWLNamedIndividual(IRI.create(indi.getName()));
			OWLClass ontoCls = df.getOWLClass(IRI.create(indi.getType().getName()));
			OWLClassAssertionAxiom classAssertion = df.getOWLClassAssertionAxiom(ontoCls, ontoIndi);
			manager.addAxiom(ontology, classAssertion);
			manager.saveOntology(ontology);
		}
		this.createObjectPropertyAssertions(individuals);
		this.createDataPropertyAssertion(individuals);
	}
	
	private void createObjectPropertyAssertions(List<MyOWLIndividual> individuals) throws OWLOntologyStorageException {
		Iterator<MyOWLIndividual> iter = individuals.iterator();
		while(iter.hasNext()) {
			MyOWLIndividual indi = iter.next();
			OWLIndividual ontoIndi = df.getOWLNamedIndividual(IRI.create(indi.getName()));
			if(indi.getObjAssociations().size() > 0) {
				Iterator<ObjPropIndiAssociation> asocIter = indi.getObjAssociations().iterator();
				while(asocIter.hasNext()) {
					ObjPropIndiAssociation asoc = asocIter.next();
					MyOWLObjectProperty prop = asoc.getObjProperty();
					MyOWLIndividual indiAsoc = asoc.getIndividual();
					OWLObjectProperty ontoObj = df.getOWLObjectProperty(IRI.create(prop.getName()));
					OWLIndividual ontoIndiAsoc = df.getOWLNamedIndividual(IRI.create(indiAsoc.getName()));
					OWLAxiom assertion = df.getOWLObjectPropertyAssertionAxiom(ontoObj, ontoIndi, ontoIndiAsoc);
					AddAxiom addAxiomChange = new AddAxiom(ontology, assertion);
					manager.applyChange(addAxiomChange);
					manager.saveOntology(ontology);
				}
			}
		}
	}
	
	private void createDataPropertyAssertion(List<MyOWLIndividual> individuals) throws OWLOntologyStorageException {
		Iterator<MyOWLIndividual> iter = individuals.iterator();
		while(iter.hasNext()) {
			MyOWLIndividual indi = iter.next();
			OWLIndividual ontoIndi = df.getOWLNamedIndividual(IRI.create(indi.getName()));
			if(indi.getDataAssociations().size() > 0) {
				Iterator<DataPropIndiAssociation> asocIter = indi.getDataAssociations().iterator();
				while(asocIter.hasNext()) {
					DataPropIndiAssociation asoc = asocIter.next();
					MyOWLDataProperty prop = asoc.getDataProperty();
					String value = asoc.getValue();
					OWLDataProperty ontoData = df.getOWLDataProperty(IRI.create(prop.getName()));
					OWLAxiom axi = df.getOWLDataPropertyAssertionAxiom(ontoData, ontoIndi, value);
					AddAxiom addAxiom = new AddAxiom(ontology, axi);
					manager.applyChange(addAxiom);
				}
			}
		}
		manager.saveOntology(ontology);
	}
	
	private void test() throws OWLOntologyStorageException {
//		org.semanticweb.owlapi.model.OWLIndividual indi = df.getOWLNamedIndividual(IRI.create("marek"));
//		org.semanticweb.owlapi.model.OWLIndividual subindi = df.getOWLNamedIndividual(IRI.create("hruska"));
//		org.semanticweb.owlapi.model.OWLObjectProperty ontoProp = df.getOWLObjectProperty(IRI.create("d"));
//		OWLAxiom axiom = df.getOWLObjectPropertyAssertionAxiom(ontoProp, indi, subindi);
//		manager.addAxiom(ontology, axiom);
//		manager.saveOntology(ontology);
//		
//		 <j.0:Hospital rdf:ID="DepartureHospital">
//		    <j.0:hasAddress>
//		      <j.0:Address rdf:ID="DepartureHospitalAddress"/>
//		    </j.0:hasAddress>
//		    <j.0:hasName>Distrital Faro</j.0:hasName>
//		  </j.0:Hospital>
		
		org.semanticweb.owlapi.model.OWLIndividual matthew = df.getOWLNamedIndividual(IRI.create("#matthew"));
		org.semanticweb.owlapi.model.OWLIndividual peter = df.getOWLNamedIndividual(IRI.create("#peter"));
	
		// We need the hasFather property
		org.semanticweb.owlapi.model.OWLObjectProperty hasFather = df.getOWLObjectProperty(IRI.create("#d"));
		
		OWLDataProperty hasName = df.getOWLDataProperty(IRI.create("#hasName"));
		OWLAxiom axi = df.getOWLDataPropertyAssertionAxiom(hasName, matthew, "nejake meno");
		AddAxiom addAxiom = new AddAxiom(ontology, axi);
		manager.applyChange(addAxiom);
		
		
		// matthew -> hasFather -> peter
		OWLAxiom assertion = df.getOWLObjectPropertyAssertionAxiom(hasFather, matthew, peter);
		// Finally, add the axiom to our ontology
		AddAxiom addAxiomChange = new AddAxiom(ontology, assertion);
		manager.applyChange(addAxiomChange);
		
		org.semanticweb.owlapi.model.OWLClass ontoCls = df.getOWLClass(IRI.create("#Parent"));
		OWLClassAssertionAxiom classAssertion1 = df.getOWLClassAssertionAxiom(ontoCls, peter);
		OWLClassAssertionAxiom classAssertion2 = df.getOWLClassAssertionAxiom(ontoCls, matthew);
		manager.addAxiom(ontology, classAssertion1);
		manager.addAxiom(ontology, classAssertion2);
		
		manager.saveOntology(ontology);
	}
}
